package mainServer;


import interaction.BDConnection;
import interaction.BDDisconnect;
import interaction.SendBD;
import interaction.SendRes;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;

import utils.StreamUtils;

public class TcpServer extends DefaultServer
{      
  private static final int TIMEOUT = 1000;  
  private int m_port;
  private boolean m_isActive = false;
  private Listener m_listener = new Listener();  
  ServerSocket m_serverSocket;  
  
  public TcpServer(int port)
  {
    m_port = port;    
  }
  
  private class SocketListener implements Runnable
  {
    Socket m_socket;
    private boolean m_isAlive = true;
    private java.io.InputStream m_input;
    private java.io.OutputStream m_output;
    private Integer m_id = -1;
    
    public SocketListener(Socket sock)
    {
      m_socket = sock;      
    }
    
    private void initStreams()
    {
      try
      {
        m_socket.setSoTimeout(TIMEOUT);
      }
      catch (SocketException e)
      {
        System.out.println("Ошибка установки таймаута у принятого клиента");
        m_isAlive = false;
      }
      
      try
      {
        m_input = m_socket.getInputStream();
      }
      catch (IOException e)
      {
        System.out.println("Ошибка получения входного потока у принятого клиента");
        m_isAlive = false;
      }
      
      try
      {
        m_output = m_socket.getOutputStream();
      }
      catch (IOException e)
      {
        System.out.println("Ошибка получения выходного потока у принятого клиента");
        m_isAlive = false;
      }
    }
    
    @Override
    public void run()
    {
      initStreams();
      
      while(m_isActive && m_isAlive)
      {
        Object obj = StreamUtils.getStreamObject(m_input);       

        if(obj != null)    
        {          
          if(obj instanceof BDConnection)
          {
            m_id = addClient((BDConnection)obj);
            ((BDConnection)obj).m_clientId = m_id;
            sendConnection((BDConnection)obj);
          }
          else if(obj instanceof BDDisconnect)
          {
            removeClient((BDDisconnect)obj);
            m_isAlive = false;
          }
          else if(obj instanceof SendBD)
          {
        	  
              SendRes findRes = new SendRes();
              findRes.m_clientId=((SendBD)obj).m_clientId;
        	  
              if(StreamUtils.fillStream(m_output, findRes) == true)
            	  System.out.println("Запрос обработан" + m_socket.getInetAddress().toString());         
              else
            	  System.out.println("Ошибка передачи данных клиенту " + m_socket.getInetAddress().toString());
          }
          else
            System.out.println("Передана необрабатываемая логика");
        }
        else
          m_isAlive = false;
        
        if((m_id != -1) && (m_clients.containsKey(m_id) == false))
          m_isAlive = false;
      }
      
      try
      {
        m_socket.close();
      }
      catch (IOException e)
      {
        System.out.println("Закрытие уже недействительного сокета");
      }
    }   
    
    private void sendConnection(BDConnection quaryConn)
    {      
      if(StreamUtils.fillStream(m_output, quaryConn) == true)      
        System.out.println("Передан ответ на соединение " + m_socket.getInetAddress().toString());      
    }
}
  
  private class Listener implements Runnable
  {     
    @Override
    public void run()
    {
      while(m_isActive)
      {
        try
        {         
          Socket sock = m_serverSocket.accept();          

          Thread newClient = new Thread(new SocketListener(sock));
          
          newClient.start();
        }
        catch (SocketTimeoutException e)
        {
          if(m_isActive == true)
          {
            //System.out.println("Нет запросов на соединение от клиентов в течение 1 секунды");
          }
        }
        catch (IOException e)
        {
          m_isActive = false;
          System.out.println("Ошибка принятия данных из серверного TCP сокета");
        }       
      }     
      
      try
      {
        m_serverSocket.close();
      }
      catch (IOException e)
      {
        System.out.println("Закрытие уже недействительного сокета");
      }
    }
  }
  
  @Override
  public boolean start()
  {
    if(m_isActive == true)
    {
      System.out.println("Попытка повторного создания TCP сокета");
      return true;
    }

    try
    {
      m_serverSocket = new ServerSocket(m_port);
    }
    catch (IOException e)
    {
      System.out.println("Ошибка создания TCP сокета, порт занят");      
      return false;
    }

    Thread listener = new Thread(m_listener);
    
    try
    {
      m_serverSocket.setSoTimeout(TIMEOUT);
    }
    catch (SocketException e)
    {            
      System.out.println("Ошибка установки таймаута");
      return false;
    }
    
    listener.start();
    m_isActive = true;
    System.out.println("Создан TCP сокет");
    
    return true;
  }

  @Override
  public boolean stop()
  {
    if(m_isActive == false)
    {
      System.out.println("Попытка остановки неинициализированного TCP сокета");
      return true;
    }    
    
    m_isActive = false;
    
    return true;
  }
}
